package com.zijidelu.luframework.starter.scanner;

import cn.hutool.core.util.StrUtil;
import com.zijidelu.luframework.utils.spring.beans.register.SpringDynamicBeanRegisterUtil;
import com.zijidelu.luframework.base.exception.SysException;
import com.zijidelu.luframework.constant.ClassNameConstant;
import lombok.Data;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDefinition;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 自动扫描器抽象类
 *
 * @author ZIJIDELU
 * @datetime 2025/10/20 23:17
 */
@Slf4j
@Data
public abstract class AbstractAutoScanner {
    private static final Map<String, Class<?>> BUILD_CLASS_CACHE = new ConcurrentHashMap<>();
    private static final Map<String, Object> LOCKS = new ConcurrentHashMap<>();
    protected final Class<?> SCANNER_CLASS = this.getClass();
    private final AtomicBoolean initialized = new AtomicBoolean(false);

    @Setter(onMethod_ = {@Autowired})
    private SpringDynamicBeanRegisterUtil springDynamicBeanRegisterUtil;

    /**
     * 计算唯一域名
     *
     * @param uniClass 唯一类
     * @return 唯一域名
     */
    public static String calcUniDomain(Class<?> uniClass, String funcName) {
        return ClassNameConstant.calcDomainName(uniClass) + funcName + "$" + Integer.toHexString(System.identityHashCode(uniClass));
    }

    /**
     * 获取扫描器名称
     *
     * @return 扫描器名称
     */
    public String getScannerName() {
        return SCANNER_CLASS.getSimpleName();
    }

    /**
     * 获取当前扫描器依赖的其他扫描器
     * <p>
     * 用于判断扫描器执行顺序，确保依赖的扫描器先执行。
     *
     * @return 依赖的其他扫描器类
     */
    public Class<? extends AbstractAutoScanner> dependOn() {
        return null;
    }

    /**
     * 清除缓存
     * <p>
     * 注意：该方法必须在所有扫描器执行完毕才可以调用
     */
    public static void clearCache() {
        BUILD_CLASS_CACHE.clear();
        LOCKS.clear();
    }

    /**
     * 初始化自动扫描器
     */
    public void init() {
        if (initialized.compareAndSet(false, true)) {
            start();
        }
    }

    /**
     * 自动扫描器开始扫描
     */
    public abstract void start();

    /**
     * 通用扫描器，用于扫描数据仓库接口，生成实现类并注册到Spring上下文。
     *
     * @param className      类名
     * @param packageName    包名
     * @param subClass       子类
     * @param interfaceClass 接口类
     */
    protected Object genericRegisterBeans(String className, String packageName, TypeDefinition subClass, TypeDefinition interfaceClass) {

        final String lowerFirstClassName = StrUtil.lowerFirst(className);
        Optional<?> beanIfExists = springDynamicBeanRegisterUtil.getBeanIfExists(lowerFirstClassName);
        if (beanIfExists.isPresent()) {
            return beanIfExists.get();
        }

        final String fullClassName = packageName + "." + className;
        final Class<?> builtClass = BUILD_CLASS_CACHE.get(fullClassName);
        // 类已存在则直接从Spring容器获取Bean返回
        if (builtClass != null) {
            // 注册bean到Spring上下文
            springDynamicBeanRegisterUtil.registerAndInitializeBean(lowerFirstClassName, builtClass);

            return springDynamicBeanRegisterUtil.getBeanIfExists(lowerFirstClassName).orElseThrow(() -> new SysException("Bean未注册: " + lowerFirstClassName));
        }
        // 使用要生成的类的全限定名作为锁对象
        final Object lock = LOCKS.computeIfAbsent(fullClassName, k -> new Object());
        synchronized (lock) {
            if (!springDynamicBeanRegisterUtil.containsBean(lowerFirstClassName)) {
                // 创建动态子类
                final DynamicType.Unloaded<?> classDynamicType = new ByteBuddy()
                        // 1. 创建泛型参数化的子类
                        .subclass(subClass)
                        // 2. 让子类同时实现泛型接口
                        .implement(interfaceClass)
                        // 3. 指定生成类的全限定名
                        .name(fullClassName)
                        // 5. 编译并加载到当前类加载器，使用默认注入策略
                        .make();

                final Class<?> buildClass = classDynamicType
                        .load(SCANNER_CLASS.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
                        .getLoaded();
                // 缓存生成的类
                BUILD_CLASS_CACHE.put(fullClassName, buildClass);

                // 注册bean到Spring上下文
                springDynamicBeanRegisterUtil.registerAndInitializeBean(lowerFirstClassName, buildClass);

            }

        }

        return springDynamicBeanRegisterUtil.getBeanIfExists(lowerFirstClassName).orElseThrow(() -> new SysException("Bean未注册: " + lowerFirstClassName));
    }

}
